/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Copyright (C) 2020 ScyllaDB
 */

#include <seastar/core/future.hh>
#include <seastar/core/reactor.hh>
#include <seastar/core/report_exception.hh>
#include <seastar/util/backtrace.hh>

namespace seastar {
namespace internal {

promise_base::promise_base(promise_base&& x) noexcept
    : _future(x._future), _state(x._state), _task(std::exchange(x._task, nullptr)) {
    x._state = nullptr;
    if (auto* fut = _future) {
        fut->detach_promise();
        fut->_promise = this;
    }
}

promise_base::~promise_base() noexcept {
    if (_future) {
        assert(_state);
        assert(_state->available() || !_task);
        _future->detach_promise();
    } else if (__builtin_expect(bool(_task), false)) {
        assert(_state && !_state->available());
        _state->set_to_broken_promise();
        ::seastar::schedule(std::exchange(_task, nullptr));
    }
}

template <promise_base::urgent Urgent>
void promise_base::make_ready() noexcept {
    if (_task) {
        _state = nullptr;
        if (Urgent == urgent::yes && !need_preempt()) {
            ::seastar::schedule_urgent(std::exchange(_task, nullptr));
        } else {
            ::seastar::schedule(std::exchange(_task, nullptr));
        }
    }
}

template void promise_base::make_ready<promise_base::urgent::no>() noexcept;
template void promise_base::make_ready<promise_base::urgent::yes>() noexcept;

template
future<> internal::current_exception_as_future() noexcept;
}

/**
 * engine_exit() exits the reactor. It should be given a pointer to the
 * exception which prompted this exit - or a null pointer if the exit
 * request was not caused by any exception.
 */
void engine_exit(std::exception_ptr eptr) {
    if (!eptr) {
        engine().exit(0);
        return;
    }
    report_exception("Exiting on unhandled exception", eptr);
    engine().exit(1);
}

broken_promise::broken_promise() : logic_error("broken promise") { }

future_state_base future_state_base::current_exception() {
    return future_state_base(std::current_exception());
}

void future_state_base::set_to_broken_promise() noexcept {
    try {
        // Constructing broken_promise may throw (std::logic_error ctor is not noexcept).
        set_exception(std::make_exception_ptr(broken_promise{}));
    } catch (...) {
        set_exception(std::current_exception());
    }
}

void report_failed_future(const std::exception_ptr& eptr) noexcept {
    ++engine()._abandoned_failed_futures;
    seastar_logger.warn("Exceptional future ignored: {}, backtrace: {}", eptr, current_backtrace());
}

void report_failed_future(const future_state_base& state) noexcept {
    report_failed_future(state._u.ex);
}

}
